package com.mike.ad.service.impl;

import com.mike.ad.constant.Constants;
import com.mike.ad.dao.AdCreativeRepository;
import com.mike.ad.dao.AdPlanRepository;
import com.mike.ad.dao.AdUnitRepository;
import com.mike.ad.dao.unit_condition.AdCreativeUnitRepository;
import com.mike.ad.dao.unit_condition.AdUnitDistrictRepository;
import com.mike.ad.dao.unit_condition.AdUnitItRepository;
import com.mike.ad.dao.unit_condition.AdUnitKeyWordRepository;
import com.mike.ad.entity.AdPlan;
import com.mike.ad.entity.AdUnit;
import com.mike.ad.entity.unit_condition.AdCreativeUnit;
import com.mike.ad.entity.unit_condition.AdUnitDistrict;
import com.mike.ad.entity.unit_condition.AdUnitIt;
import com.mike.ad.entity.unit_condition.AdUnitKeyword;
import com.mike.ad.exception.AdException;
import com.mike.ad.service.IAdUnitService;
import com.mike.ad.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class AdUnitServiceImpl implements IAdUnitService {

    private final AdPlanRepository planRepository;
    private final AdUnitRepository unitRepository;
    private final AdUnitKeyWordRepository unitKeyWordRepository;
    private final AdUnitDistrictRepository unitDistrictRepository;
    private final AdUnitItRepository unitItRepository;
    private final AdCreativeRepository adCreativeRepository;
    private final AdCreativeUnitRepository adCreativeUnitRepository;
    @Autowired
    public AdUnitServiceImpl(AdUnitRepository unitRepository,
                             AdPlanRepository planRepository,
                             AdUnitKeyWordRepository unitKeyWordRepository,
                             AdUnitDistrictRepository unitDistrictRepository,
                             AdUnitItRepository unitItRepository,
                             AdCreativeUnitRepository adCreativeUnitRepository,
                             AdCreativeRepository adCreativeRepository) {
        this.unitRepository = unitRepository;
        this.planRepository = planRepository;
        this.unitKeyWordRepository = unitKeyWordRepository;
        this.unitDistrictRepository = unitDistrictRepository;
        this.unitItRepository = unitItRepository;
        this.adCreativeUnitRepository = adCreativeUnitRepository;
        this.adCreativeRepository = adCreativeRepository;
    }

    @Override
    @Transactional
    public AdUnitResponse createUnit(AdUnitRequest request)
            throws AdException {
        if (!request.createValidate()) {
            throw new AdException(Constants.ErrorMsg.REQUEST_PARAM_ERROR);
        }

        Optional<AdPlan> plan = planRepository.findById(request.getPlanId());
        if (!plan.isPresent()) {
            throw new AdException(Constants.ErrorMsg.CAN_NOT_FIND_RECODE);
        }
        AdUnit unit = unitRepository.findByPlanIdAndUnitName(request.getPlanId(),
                request.getUnitName());
        if (null != unit) {
            throw new AdException(Constants.ErrorMsg.SAME_NAME_UNIT_ERROR);
        }
        AdUnit newUnit = unitRepository.save(new AdUnit(request.getPlanId(),
                request.getUnitName(), request.getPositionType(),
                request.getBudget()));
        return new AdUnitResponse(newUnit.getId(), newUnit.getUnitName());
    }

    @Override
    @Transactional
    public AdUnitKeyWordResponse createUnitKeyword(AdUnitKeywordRequest request)
            throws AdException {
        List<Long> unitIds = request.getUnitkeywords().stream()
                .map(AdUnitKeywordRequest.Unitkeyword::getUnitId)
                .collect(Collectors.toList());
        if (!isRelatedUnitExist(unitIds)) {
            throw new AdException(Constants.ErrorMsg.REQUEST_PARAM_ERROR);
        }

        List<Long> ids = Collections.emptyList();
        List<AdUnitKeyword> unitKeywords = new ArrayList<>();
        if (!CollectionUtils.isEmpty(request.getUnitkeywords())) {
            request.getUnitkeywords().forEach(i -> unitKeywords.add(
                    new AdUnitKeyword(i.getUnitId(), i.getKeyword())
            ));
            ids = unitKeyWordRepository.saveAll(unitKeywords).stream()
                    .map(AdUnitKeyword::getId).collect(Collectors.toList());
        }
        return new AdUnitKeyWordResponse(ids);
    }

    @Override
    @Transactional
    public AdUnitItResponse createUnitIt(AdUnitItRequest request)
            throws AdException {
        List<Long> unitIds = request.getUnitIts().stream()
                .map(AdUnitItRequest.UnitIt::getUnitId)
                .collect(Collectors.toList());
        if (!isRelatedUnitExist(unitIds)) {
            throw new AdException(Constants.ErrorMsg.REQUEST_PARAM_ERROR);
        }
        List<Long> ids = Collections.emptyList();
        List<AdUnitIt> unitItList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(request.getUnitIts())) {
            request.getUnitIts().forEach(i -> unitItList.add(
                    new AdUnitIt(i.getUnitId(), i.getItTag())
            ));
            ids = unitItRepository.saveAll(unitItList).stream()
                    .map(AdUnitIt::getId).collect(Collectors.toList());
        }
        return new AdUnitItResponse(ids);
    }

    @Override
    @Transactional
    public AdUnitDistrictResponse createUnitDistrict(AdUnitDistrictRequest request) throws AdException {
        List<Long> unitIds = request.getUnitDistricts().stream()
                .map(AdUnitDistrictRequest.UnitDistrict::getUnitId)
                .collect(Collectors.toList());
        if (!isRelatedUnitExist(unitIds)) {
            throw new AdException(Constants.ErrorMsg.REQUEST_PARAM_ERROR);
        }
        List<Long> ids = Collections.emptyList();
        List<AdUnitDistrict> unitDistricts = new ArrayList<>();
        if (!CollectionUtils.isEmpty(request.getUnitDistricts())) {
            request.getUnitDistricts().forEach(i -> unitDistricts.add(
                    new AdUnitDistrict(i.getUnitId(),
                            i.getCity(), i.getProvince())
            ));
            ids = unitDistrictRepository.saveAll(unitDistricts).stream()
                    .map(AdUnitDistrict::getId).collect(Collectors.toList());
        }
        return new AdUnitDistrictResponse(ids);
    }

    @Override
    @Transactional
    public AdCreativeUnitResponse createCreativeUnit(
            AdCreativeUnitRequest request) throws AdException {

        List<Long> unitIds = request.getUnitItems().stream()
                .map(AdCreativeUnitRequest.AdCreativeUnitItem::getUnitId)
                .collect(Collectors.toList());
        List<Long> creativeIds = request.getUnitItems().stream()
                .map(AdCreativeUnitRequest.AdCreativeUnitItem::getCreativeId)
                .collect(Collectors.toList());

        if (!(isRelatedUnitExist(unitIds) && isRelatedCreativeExist(creativeIds))) {
            throw new AdException(Constants.ErrorMsg.REQUEST_PARAM_ERROR);
        }

        List<AdCreativeUnit> creativeUnits = new ArrayList<>();
        request.getUnitItems().forEach(i -> creativeUnits.add(
                new AdCreativeUnit(i.getCreativeId(), i.getUnitId())
        ));

        List<Long> ids = adCreativeUnitRepository.saveAll(creativeUnits)
                .stream()
                .map(AdCreativeUnit::getId)
                .collect(Collectors.toList());

        return new AdCreativeUnitResponse(ids);
    }

    private boolean isRelatedUnitExist(List<Long> unitIds) {
        if (CollectionUtils.isEmpty(unitIds)) {
            return false;
        }
        return unitRepository.findAllById(unitIds).size()
                == new HashSet<>(unitIds).size();
    }

    private boolean isRelatedCreativeExist(List<Long> creativeIds) {

        if (CollectionUtils.isEmpty(creativeIds)) {
            return false;
        }

        return adCreativeRepository.findAllById(creativeIds).size() ==
                new HashSet<>(creativeIds).size();
    }
}
